<?php

namespace pmvc\util;

use Exception;

class StringUtil {

	const ESCAPE_NONE	= 0;
	const ESCAPE_URL	= 1;
	const ESCAPE_HTML	= 2;

	/**
	 * Like pstring except that it takes an array as
	 * an argument.
	 * @param $str
	 * @param $args
	 * @return the string
	 */
	public static function pstringArray($str, $args) {
		array_unshift($args, $str);
	    return call_user_func_array("pmvc\\util\\StringUtil::pstring", $args);
	}
	
	/**
	 * Parses a string and adds the arguments.
	 * @param $str
	 * @param $args
	 * @return the string
	 */
	public static function pstring() {
	    $n = func_num_args();
	    $a = func_get_args();
	    if ($n==0) {
	    	throw new Exception("must pass at least 1 argument to pstring");
	    }
	    $str = $a[0];
		return preg_replace_callback('/\{([0-9]+)\}/', function($matches)use($a) {
			return $a[intval($matches[1])+1];
		}, $str);
	}

	/**
	 * Regex matcher.
	 */
	public static function matches($regex, $subject, $ignore_case=FALSE) {
		return preg_match("\1".$regex."\1".(($ignore_case)?"i":""), $subject) > 0;
	}

	/**
	 * Checks if a string starts with another string.
	 * 
	 * @param string $main_str the main string being compared
	 * @param string $str the secondary string being compared
	 * @param boolean $case_insensitivity if case_insensitivity is TRUE, comparison is case insensitive
	 */
	public static function startsWith($main_str, $str, $case_insensitivity=FALSE) {
		$str_len = strlen($str);
		if (strlen($main_str) < $str_len) {
			return FALSE;
		}
		return (substr_compare($main_str, $str, 0, $str_len, $case_insensitivity) === 0);
	}

	/**
	 * Checks if a string ends with another string.
	 * 
	 * @param string $main_str the main string being compared
	 * @param string $str the secondary string being compared
	 * @param boolean $case_insensitivity if case_insensitivity is TRUE, comparison is case insensitive
	 */
	public static function endsWith($main_str, $str, $case_insensitivity=FALSE) {
		$str_len = strlen($str);
		if (strlen($main_str) < $str_len) {
			return FALSE;
		}
		return (substr_compare($main_str, $str, ($str_len * -1), $str_len, $case_insensitivity) === 0);
	}

	/**
	 * Extracts matches from a string using a named subpattern regular expression, and maps the
	 * matching group values to parameter names. The pattern must contain one or more named 
	 * subpatterns and pattern-match the input string.
	 * 
	 * @param string $str the string to parse
	 * @param string $pattern the regular expression pattern
	 * @return an array of parameter values mapped to their names
	 */
	public static function extractNamedMatches($str, $pattern) {
		
		if (strpos($pattern, '(?<') === FALSE) {
			// pattern does not contain named subpatterns
			return NULL;
		}
		
		$result = preg_match("/$pattern/", $str, $matches);

		if ($result != 1) {
			// pattern didn't match input string
			return NULL;
		}
		
		// return the name-value pairs
		$ret = Array();
		foreach ($matches as $key => $value) {
			if (!is_int($key)) {
				$ret[$key] = $value;
			}
		}
		return $ret;
	}
	
	/**
	 * Replaces named subpatterns in a regular expression, using an associative name-value array.
	 * 
	 * @param string $url the URL to parse
	 * @param string $pattern the regular expression pattern
	 * @return an array of parameter values mapped to their names
	 */
	public static function replaceNamedSubpatterns($pattern, $nameValues, $escape=self::ESCAPE_NONE) {
		
		// remove leading pattern characters (/^)
		$ret = preg_replace('/^\\/?\\^?/', '', $pattern);
		// remove trailing pattern characters ($/)
		$ret = preg_replace('/\\$?\\/?$/', '', $ret);
		
		foreach ($nameValues as $key => $value) {
			// subpattern matcher using non-greedy match and negative lookbehind 
			// to ignore escaped parentheses
			$p = '\\(\\?<' . $key . '>.*?(?<!\\\\)\\)';
			
			if ($escape != self::ESCAPE_NONE) {
				// escape the replacement value
				switch ($escape) {
					case self::ESCAPE_URL:
						$value = urlencode($value);
						break;
					case self::ESCAPE_HTML:
						$value = htmlentities($value, ENT_QUOTES);
						break;
				}
			}
			
			$ret = preg_replace("/$p/", $value, $ret);
		}
		
		// unescape
		$ret = preg_replace('/\\\\([^\\\\])/', '$1', $ret);
		
		return $ret;
	}

	/**
	 * Converts an ASCII string into a hexadecimal string, with each ASCII character 
	 * represented by two hexadecimal characters.
	 * 
	 * @param string $str the source string
	 * @return the hexadecimal string
	 */
	public static function strToHex($str) {
		$ret = '';
		for ($i = 0, $str_len = strlen($str); $i < $str_len; ++$i) {
			$ret .= dechex(ord($str[$i]));
		}
		return $ret;
	}
}


?>